home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Applications / NIH Image 1.59 / 1.59 Source / Stacks.p < prev    next >
Encoding:
Text File  |  1995-10-26  |  56.0 KB  |  1,337 lines  |  [TEXT/PJMM]

  1. unit Stacks;
  2.  
  3. interface
  4.  
  5.     uses
  6.         Memory, QuickDraw, Packages, Menus, Events, Fonts, Scrap, ToolUtils,Resources, Errors, Palettes, QDOffscreen, PictUtils, Timer, globals, Utilities, Graphics, Analysis, Camera, file1, file2, filters, sound, lut;
  7.  
  8.     procedure MakeStack;
  9.     procedure MakeWindowsFromStack;
  10.     function AddSlice (update: boolean): boolean;
  11.     procedure DeleteSlice;
  12.     procedure ShowNextSlice (item: integer);
  13.     procedure ShowFirstOrLastSlice (ich: integer);
  14.     procedure DoStackInfo;
  15.     procedure Reslice;
  16.     procedure Animate;
  17.     procedure MakeMovie(ShowDialog: boolean);
  18.     procedure CaptureFrames;
  19.     procedure MakeMontage;
  20.     procedure ConvertRGBToEightBitColor (Capturing: boolean);
  21.     procedure ConvertEightBitColorToRGB;
  22.     procedure CaptureColor;
  23.     procedure AverageSlices;
  24.     procedure ConvertRGBToHSV;
  25.  
  26.  
  27. implementation
  28.  
  29.  
  30.     procedure MakeStack;
  31.         var
  32.             ok, isStack: boolean;
  33.             i, result: integer;
  34.             TempInfo, SaveInfo: InfoPtr;
  35.             str: str255;
  36.     begin
  37.         if not AllSameSize then begin
  38.                 PutError('All currently open images must be the same size to make a stack.');
  39.                 exit(MakeStack);
  40.             end;
  41.         isStack := false;
  42.         for i := 1 to nPics do begin
  43.                 TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
  44.                 isStack := isStack or (TempInfo^.StackInfo <> nil);
  45.             end;
  46.         if isStack then begin
  47.                 PutError('All stacks must be closed before making a new stack.');
  48.                 exit(MakeStack);
  49.             end;
  50.         if nPics > MaxSlices then begin
  51.                 NumToString(MaxSlices, str);
  52.                 PutError(concat('Maximun stack size is ', str, ' slices.'));
  53.                 exit(MakeStack);
  54.             end;
  55.         StopDigitizing;
  56.         DisableDensitySlice;
  57.         SelectWindow(PicWindow[1]);
  58.         Info := pointer(WindowPeek(PicWindow[1])^.RefCon);
  59.         ActivateWindow;
  60.         KillRoi;
  61.         UnZoom;
  62.         if not MakeStackFromWindow then
  63.             exit(MakeStack);
  64.         with info^ do begin
  65.                 StackInfo^.nSlices := nPics;
  66.                 title := 'Stack';
  67.                 UpdateTitleBar;
  68.                 Revertable := false;
  69.             end;
  70.         SaveInfo := Info;
  71.         MakingStack := true;
  72.         ShowWatch;
  73.         for i := 2 to nPics do begin
  74.                 TempInfo := pointer(WindowPeek(PicWindow[2])^.RefCon);
  75.                 with TempInfo^ do begin
  76.                         hunlock(PicBaseHandle);
  77.                         info^.StackInfo^.PicBaseH[i] := PicBaseHandle;
  78.                     end;
  79.                 result := CloseAWindow(PicWindow[2]);
  80.                 Info := SaveInfo;
  81.             end;
  82.         UpdateWindowsMenuItem;
  83.         MakingStack := false;
  84.     end;
  85.  
  86.  
  87.     procedure DeleteSlice;
  88.         var
  89.             SliceToDelete, NextSlice, i: integer;
  90.             isRoi: boolean;
  91.     begin
  92.         with info^, info^.StackInfo^ do begin
  93.                 if nSlices = 1 then begin
  94.                         WhatToUndo := NothingToUndo;
  95.                         exit(DeleteSlice);
  96.                     end;
  97.                 isRoi := RoiShowing;
  98.                 if isRoi then
  99.                     KillRoi;
  100.                 SetupUndo;
  101.                 WhatToUndo := UndoSliceDelete;
  102.                 SliceToDelete := CurrentSlice;
  103.                 if CurrentSlice = 1 then begin
  104.                         NextSlice := 2;
  105.                         WhatToUndo := UndoFirstSliceDelete;
  106.                     end
  107.                 else
  108.                     NextSlice := CurrentSlice - 1;
  109.                 SelectSlice(NextSlice);
  110.                 UpdatePicWindow;
  111.                 DisposeHandle(PicBaseH[SliceToDelete]);
  112.                 for i := SliceToDelete to nSlices - 1 do
  113.                     PicBaseH[i] := PicBaseH[i + 1];
  114.                 nSlices := nSlices - 1;
  115.                 if CurrentSlice <> 1 then
  116.                     CurrentSlice := CurrentSlice - 1;
  117.                 if (StackType = rgbStack) and (nSlices <> 3) then
  118.                     StackType := VolumeStack;
  119.                 UpdateTitleBar;
  120.                 if isRoi then
  121.                     RestoreRoi;
  122.                 changes := true;
  123.                 UpdateWindowsMenuItem;
  124.             end;
  125.     end;
  126.  
  127.  
  128.     procedure MakeWindowsFromStack;
  129.         var
  130.             i, ignore: integer;
  131.             N: LongInt;
  132.             SaveInfo: InfoPtr;
  133.             tmp: longint;
  134.  
  135.         function MakeName (i: integer): str255;
  136.             var
  137.                 str: str255;
  138.         begin
  139.             RealToString(i, 3, 0, str);
  140.             if str[1] = ' ' then
  141.                 str[1] := '0';
  142.             if str[2] = ' ' then
  143.                 str[2] := '0';
  144.             MakeName := str;
  145.         end;
  146.  
  147.     begin
  148.         N := info^.StackInfo^.nSlices;
  149.         tmp := SizeOf(PicInfo);
  150.         if MaxBlock < (MinFree + info^.ImageSize + (SizeOf(PicInfo) + 2000) * N) then begin
  151.                 PutError('There is not enough memory available to convert this stack to windows.');
  152.                 exit(MakeWindowsFromStack);
  153.             end;
  154.         SaveInfo := Info;
  155.         KillRoi;
  156.         for i := 1 to N - 1 do begin
  157.                 SelectSlice(1);
  158.                 info^.StackInfo^.CurrentSlice := 1;
  159.                 if not Duplicate(MakeName(i), false) then
  160.                     exit(MakeWindowsFromStack);
  161.                 info := SaveInfo;
  162.                 DeleteSlice;
  163.             end;
  164.         if Duplicate(MakeName(N), false) then begin
  165.                 info := SaveInfo;
  166.                 info^.changes := false;
  167.                 ignore := CloseAWindow(info^.wptr);
  168.             end;
  169.     end;
  170.  
  171.  
  172.     procedure ShowNextSlice (item: integer);
  173.         var
  174.             isRoi: boolean;
  175.     begin
  176.         with info^, info^.StackInfo^ do begin
  177.                 if item = NextSliceItem then begin
  178.                         CurrentSlice := CurrentSlice + 1;
  179.                         if CurrentSlice > nSlices then
  180.                             CurrentSlice := nSlices;
  181.                     end
  182.                 else begin
  183.                         CurrentSlice := CurrentSlice - 1;
  184.                         if CurrentSlice < 1 then
  185.                             CurrentSlice := 1;
  186.                     end;
  187.                 isRoi := RoiShowing;
  188.                 if isRoi then
  189.                     KillRoi;
  190.                 SelectSlice(CurrentSlice);
  191.                 UpdatePicWindow;
  192.                 UpdateTitleBar;
  193.                 WhatToUndo := NothingToUndo;
  194.                 isInsertionPoint:=false;
  195.                 if isRoi then
  196.                     RestoreRoi;
  197.             end;
  198.     end;
  199.  
  200.  
  201.     procedure ShowFirstOrLastSlice (ich: integer);
  202.         var
  203.             isRoi: boolean;
  204.     begin
  205.         with info^, info^.StackInfo^ do begin
  206.                 if ich = EndKey then
  207.                     CurrentSlice := nSlices
  208.                 else
  209.                     CurrentSlice := 1;
  210.                 isRoi := RoiShowing;
  211.                 if isRoi then
  212.                     KillRoi;
  213.                 SelectSlice(CurrentSlice);
  214.                 UpdatePicWindow;
  215.                 UpdateTitleBar;
  216.                 WhatToUndo := NothingToUndo;
  217.                 isInsertionPoint:=false;
  218.                 if isRoi then
  219.                     RestoreRoi;
  220.             end;
  221.     end;
  222.  
  223.  
  224.     procedure GetSlice (xstart, ystart, start: extended; angle: extended; count: integer; var line: LineType);
  225.         var
  226.             i: integer;
  227.             x, y, xinc, yinc: extended;
  228.             IntegerStart: boolean;
  229.     begin
  230.         IntegerStart := (xstart = trunc(xstart)) and (ystart = trunc(ystart));
  231.         if IntegerStart and (angle = 0.0) then begin
  232.                 GetLine(trunc(xstart), trunc(ystart), count, line);
  233.                 exit(GetSlice);
  234.             end;
  235.         if IntegerStart and (angle = 270.0) then begin
  236.                 GetColumn(trunc(xstart), trunc(ystart), count, line);
  237.                 exit(GetSlice);
  238.             end;
  239.         angle := (angle / 180.0) * pi;
  240.         xinc := cos(angle);
  241.         yinc := -sin(angle);
  242.         x := xstart + start * xinc;
  243.         y := ystart + start * yinc;
  244.         for i := 0 to count - 1 do begin
  245.                 line[i] := round(GetInterpolatedPixel(x, y));
  246.                 x := x + xinc;
  247.                 y := y + yinc;
  248.             end;
  249.     end;
  250.  
  251.  
  252.     function DoResliceOptions: boolean;
  253.     var
  254.         default, tmp: extended;
  255.         Canceled: boolean;
  256.         prompt, str: str255;
  257.     begin
  258.         with info^.StackInfo^, info^ do begin
  259.             if SpatiallyCalibrated then begin
  260.                 default := SliceSpacing / xScale;
  261.                 str := xUnit;
  262.             end else begin
  263.                 default := SliceSpacing;
  264.                 str := 'pixels';
  265.             end;
  266.             if SliceSpacing = 0.0 then
  267.                 default := 1.0;
  268.             tmp := GetReal(concat('Slice Spacing (', str, '):'), default, 2, Canceled);
  269.             if not Canceled and (tmp > 0.0) then begin
  270.                     if SpatiallyCalibrated then
  271.                         SliceSpacing := tmp * xScale
  272.                     else
  273.                         SliceSpacing := tmp;
  274.                 end;
  275.         end; {with}
  276.         DoResliceOptions := not canceled;
  277.     end;
  278.  
  279.  
  280.     procedure Reslice;
  281.         var
  282.             DstWidth, DstHeight, nSlices: integer;
  283.             dstLeft, dstTop, y, i, j, LineLength: integer;
  284.             SaveWindowFlag, SaveMacro, HorizontalMode: boolean;
  285.             SaveHScale, SaveVScale, UncalibratedLineLength, CalibratedLineLength, angle: extended;
  286.             Stack, Reconstruction: InfoPtr;
  287.             aLine: LineType;
  288.             name, str1, str2: str255;
  289.             MaskRect: rect;
  290.             x1, y1, x2, y2, ulength, clength: extended;
  291.  
  292.         procedure MakeRoi (Left, Top, Width, Height: integer);
  293.         begin
  294.             with info^ do begin
  295.                     RoiType := RectRoi;
  296.                     SetRect(RoiRect, left, top, left + width, top + height);
  297.                     MakeRegion;
  298.                     SetupUndo;
  299.                     RoiShowing := true;
  300.                 end;
  301.         end;
  302.  
  303.     begin
  304.         with info^, info^.StackInfo^ do begin
  305.                 if nSlices < 2 then begin
  306.                         PutError('Reslicing requires at least 2 slices.');
  307.                         AbortMacro;
  308.                         exit(Reslice);
  309.                     end;
  310.                 if not (RoiShowing and (RoiType = LineRoi)) then begin
  311.                         PutError('Please make a straight line selection first.');
  312.                         AbortMacro;
  313.                         exit(Reslice);
  314.                     end;
  315.                 Stack := info;
  316.                 GetLengthOrPerimeter(ulength, clength);
  317.                 LineLength := round(ulength);
  318.                 if LineLength = 0 then begin
  319.                         PutError('Line length cannot be zero.');
  320.                         AbortMacro;
  321.                         exit(Reslice);
  322.                     end;
  323.                 if SliceSpacing = 0.0 then
  324.                     if not DoResliceOptions then
  325.         og: DialogPtr;
  326.         item, i: integer;
  327.         FramesPerSecond: extended;
  328.         
  329.         procedure ShowFrameRate;
  330.         begin
  331.             if SecondsPerFrame = 0.0 then begin
  332.                 if fgWidth = 640 then
  333.                     FramesPerSecond := 30.0
  334.                 else FramesPerSecond := 25.0
  335.             end else
  336.                 FramesPerSecond := 1.0 / SecondsPerFrame;
  337.             if FramesPerSecond = trunc(FramesPerSecond) then
  338.                 SetDReal(MyLog, rateID, FramesPerSecond, 0)
  339.             else
  340.                 SetDReal(MyLog, rateID, FramesPerSecond, 4);
  341.         end;
  342.         
  343.         procedure ShowInterval;
  344.         begin
  345.             if SecondsPerFrame < 1.0 then
  346.                 SetDReal(MyLog, IntervalID, SecondsPerFrame, 4)
  347.             else if SecondsPerFrame < 99.0 then
  348.                 SetDReal(MyLog, IntervalID, SecondsPerFrame, 2)
  349.             else
  350.                 SetDReal(MyLog, IntervalID, SecondsPerFrame, 0);
  351.         end;
  352.         
  353.         procedure ShowTriggerMode;
  354.         begin
  355.             SetDlogItem(mylog, TriggerID, ord(ExternalTrigger));
  356.             SetDlogItem(mylog, TriggerFirstID, ord(TriggerFirstFrameOnly));
  357.             SetDlogItem(mylog, TriggerEachID, ord(not TriggerFirstFrameOnly));
  358.         end;
  359.         
  360.     begin
  361.         InitCursor;
  362.         mylog := GetNewDialog(230, nil, pointer(-1));
  363.         SetDNum(MyLog, FramesID, FramesWanted);
  364.         ShowFrameRate;
  365.         ShowInterval;
  366.         SetDlogItem(mylog, BlindID, ord(BlindMovieCapture));
  367.         SetDlogItem(mylog, LG3BufferID, ord(LG3BufferCapture));
  368.         SetDlogItem(mylog, StampID, ord(TimeStamp));
  369.         ShowTriggerMode;
  370.         SetDlogItem(mylog, UseExistingStackID, ord(UseExistingStack));
  371.         SelectDialogItemText(MyLog, FramesID, 0, 32767);
  372.         OutlineButton(MyLog, ok, 16);
  373.         repeat
  374.             ModalDialog(nil, item);
  375.             if item = FramesID then
  376.                 FramesWanted := GetDNum(MyLog, FramesID);
  377.             if item = IntervalID then begin
  378.                 SecondsPerFrame := GetDReal(MyLog, IntervalID);
  379.                 ShowFrameRate;
  380.             end;
  381.             if item = rateID then begin
  382.                 FramesPerSecond := GetDReal(MyLog, rateID);
  383.                 if FramesPerSecond <> 0.0 then
  384.                   SecondsPerFrame := 1.0 / FramesPerSecond;
  385.                 ShowInterval;
  386.             end;
  387.             if item = BlindID then begin
  388.                     BlindMovieCapture := not BlindMovieCapture;
  389.                     SetDlogItem(mylog, BlindID, ord(BlindMovieCapture));
  390.                 end;
  391.             if item = LG3BufferID then begin
  392.                     LG3BufferCapture := not LG3BufferCapture;
  393.                     if LG3BufferCapture then
  394.                         BlindMovieCapture := true;
  395.                     SetDlogItem(mylog, LG3BufferID, ord(LG3BufferCapture));
  396.                     SetDlogItem(mylog, BlindID, ord(BlindMovieCapture));
  397.                 end;
  398.             if item = StampID then begin
  399.                     TimeStamp := not TimeStamp;
  400.                     SetDlogItem(mylog, StampID, ord(TimeStamp));
  401.                 end;
  402.             if item = TriggerID then begin
  403.                 ExternalTrigger := not ExternalTrigger;
  404.                 SetDlogItem (mylog, TriggerID, ord (ExternalTrigger));
  405.               end;
  406.             if (item = TriggerFirstID) or (item = TriggerEachID) then begin
  407.                 TriggerFirstFrameOnly := not TriggerFirstFrameOnly;
  408.                 ExternalTrigger := true;
  409.                 ShowTriggerMode;
  410.               end;
  411.             if item = UseExistingStackID then begin
  412.                     UseExistingStack := not UseExistingStack;
  413.                     SetDlogItem(mylog, UseExistingStackID, ord(UseExistingStack));
  414.                 end;
  415.         until (item = ok) or (item = cancel);
  416.         DisposeDialog(mylog);
  417.         if FramesWanted < 1 then
  418.             FramesWanted := 1;
  419.         if FramesWanted > MaxSlices then
  420.             FramesWanted := MaxSlices;
  421.         if SecondsPerFrame < 0.0 then
  422.             SecondsPerFrame := 0.0;
  423.         if LG3BufferCapture and (item <> cancel) then begin
  424.             if FrameGrabber <> ScionLG3 then begin
  425.                 LG3BufferCapture := false;
  426.                 PutError('Capturing to an on-board frame buffer requires a Scion LG-3.');
  427.                 DoMakeMovieOptions := false;
  428.                 exit(DoMakeMovieOptions);
  429.             end;
  430.             if FramesWanted > MaxLG3Frames then begin
  431.                 FramesWanted := MaxLG3Frames;
  432.                 PutError(concat('This ', long2str(MaxLG3Frames div 2), 'MB LG-3 can capture a maximum of ', long2str(MaxLG3Frames), ' frames to its on-board buffer.'));
  433.                 DoMakeMovieOptions := false;
  434.                 exit(DoMakeMovieOptions);
  435.             end;
  436.         end;
  437.         DoMakeMovieOptions := item <> cancel;
  438.     end;
  439.  
  440.  
  441.     procedure CaptureFramesUsingTicks(SecondsPerFrame:extended; nFrames:integer; frect:rect);
  442.     var
  443.         StartTicks, NextTicks, LastTicks, interval, ticks: LongInt;
  444.         SourcePixMap: PixMapHandle;
  445.         str: str255;
  446.         frame, i: integer;
  447.         ElapsedTime, avgFrameInterval: extended;
  448.     begin
  449.         interval := round(60.0 * SecondsPerFrame);
  450.         ShowWatch;
  451.         SourcePixMap := fgPixMap;
  452.         ResetFrameGrabber;
  453.         ShowTriggerMessage;
  454.         with info^, info^.StackInfo^ do begin
  455.                 if Interval >= 30 then
  456.                     ShowMessage(CmdPeriodToStop)
  457.                 else
  458.                     DrawLabels('Frame:', 'Total:', '');
  459.                 if TimeStamp then begin
  460.                     SetPort(GrafPtr(osPort));
  461.                     TextFont(Monaco);
  462.                     TextSize(9);
  463.                 end;
  464.                 for frame := 1 to nFrames do begin
  465.                         CurrentSlice := frame;
  466.                         SelectSlice(CurrentSlice);
  467.                         if Interval >= 30 then
  468.                             UpdateTitleBar
  469.                         else
  470.                             Show2Values(CurrentSlice, nSlices);
  471.                         GetFrame;
  472.                         ticks:=TickCount;
  473.                         if (frame = 1) then begin
  474.                             StartTicks := ticks;
  475.                             NextTicks := StartTicks+interval - 3;
  476.                             if TriggerFirstFrameOnly then
  477.                                 ExternalTrigger := false;
  478.                         end else
  479.                             NextTicks := NextTicks + interval;
  480.                         if frame = nFrames then
  481.                             LastTicks := ticks;
  482.                         CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  483.                         if TimeStamp then begin
  484.                             ElapsedTime:=(ticks-StartTicks) / 60.0;
  485.                             RealToString(ElapsedTime, 9, 3, str);
  486.                             for i:=1 to 5 do
  487.                                 if str[i]=' ' then str[i]:='0';
  488.                             MoveTo(2,10);
  489.                             DrawString(str);
  490.                             PlotData^[frame]:=ElapsedTime;
  491.                         end;
  492.                         if not BlindMovieCapture then
  493.                             UpdatePicWindow;
  494.                         while TickCount < NextTicks do
  495.                             if CommandPeriod then begin
  496.                                     beep;
  497.                                     wait(60);
  498.                                     exit(CaptureFramesUsingTicks);
  499.                                 end;
  500.                     end; {for}
  501.                 ElapsedTime := (LastTicks - StartTicks) / 60.0;
  502.                 avgFrameInterval := ElapsedTime / (nFrames - 1);
  503.                 FrameInterval := avgFrameInterval;
  504.             end; {with}
  505.     end;
  506.  
  507.  
  508.  
  509.     function uTickCount:extended;
  510.     var
  511.         count:UnsignedWide;
  512.         d:extended;
  513.     begin
  514.         microseconds(count);
  515.         d:=count.lo;
  516.         if d<0 then d:=band(count.lo,$7fffffff)+2147483648.0;
  517.         uTickCount:=d+count.hi*4294967296.0;
  518.     end;
  519.  
  520.  
  521.     procedure CaptureFramesUsingMicroTicks(SecondsPerFrame:extended; nFrames:integer; frect:rect);
  522.     var
  523.         uStartTicks, uNextTicks, uLastTicks, uInterval, uTicks: Extended;
  524.         SourcePixMap: PixMapHandle;
  525.         str: str255;
  526.         frame, i: integer;
  527.         ElapsedTime: extended;
  528.         uTicksToCaptureOneFrame, avgFrameInterval:extended;
  529.         ShowProgress: boolean;
  530.     begin
  531.         ShowWatch;
  532.         uInterval := 1000000.0 * SecondsPerFrame;
  533.         SourcePixMap := fgPixMap;
  534.         ResetFrameGrabber;
  535.         ShowTriggerMessage;
  536.         if fgWidth = 768 then  {if PAL board}
  537.             uTicksToCaptureOneFrame := 40000.0  {PAL captures 25 fps}
  538.         else
  539.             uTicksToCaptureOneFrame := 33333.0;  {non-PAL captures 33 fps}
  540.         ShowProgress := (not LG3BufferCapture) or (uInterval > (2 * uTicksToCaptureOneFrame));
  541.         with info^, info^.StackInfo^ do begin
  542.                 if ShowProgress and (uInterval < 500000.0) then
  543.                     DrawLabels('Frame:', 'Total:', '')
  544.                 else if not ExternalTrigger then
  545.                     ShowMessage(CmdPeriodToStop);
  546.                 if TimeStamp then begin
  547.                     SetPort(GrafPtr(osPort));
  548.                     TextFont(Monaco);
  549.                     TextSize(9);
  550.                 end;
  551.                 for frame := 1 to nFrames do begin
  552.                     CurrentSlice := frame;
  553.                     SelectSlice(CurrentSlice);
  554.                     if showProgress then begin
  555.                         if uInterval >= 500000.0 then
  556.                             UpdateTitleBar
  557.                         else
  558.                             Show2Values(CurrentSlice, nSlices);
  559.                     end;
  560.                     if LG3BufferCapture then begin
  561.                         BufferReg^ := frame - 1;
  562.                         GetFrame;
  563.                         uTicks := uTickCount;
  564.                     end else begin
  565.                         GetFrame;
  566.                         uTicks := uTickCount;
  567.                         CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  568.                     end;
  569.                     if frame = 1 then begin
  570.                         uStartTicks := uTicks;
  571.                         uNextTicks := uStartTicks + uInterval - 1.5 * uTicksToCaptureOneFrame;
  572.                         if TriggerFirstFrameOnly then
  573.                             ExternalTrigger := false;
  574.                     end else
  575.                         uNextTicks :=uNextTicks + uInterval;
  576.                     if frame = nFrames then
  577.                         uLastTicks := uTicks;
  578.                     if TimeStamp then begin
  579.                         ElapsedTime:=(uTicks-uStartTicks) / 1000000.0;
  580.                         PlotData^[frame]:=ElapsedTime;
  581.                         if not LG3BufferCapture then begin
  582.                             RealToString(ElapsedTime, 9, 3, str);
  583.                             for i:=1 to 5 do
  584.                                 if str[i]=' ' then str[i]:='0';
  585.                             MoveTo(2,10);
  586.                             DrawString(str);
  587.                         end;
  588.                     end;
  589.                     if not BlindMovieCapture then
  590.                         UpdatePicWindow;
  591.                     if uTicks < uNextTicks then
  592.                         while uTickCount < uNextTicks do
  593.                             if CommandPeriod then begin
  594.                                     beep;
  595.                                     wait(60);
  596.                                     exit(CaptureFramesUsingMicroTicks);
  597.                                 end;
  598.                     end; {for}
  599.                 ElapsedTime := (uLastTicks - uStartTicks) / 1000000.0;
  600.                 avgFrameInterval := ElapsedTime / (nFrames - 1);
  601.                 FrameInterval := avgFrameInterval;
  602.             end; {with}
  603.         if LG3BufferCapture then begin
  604.             {Copy captured frames from LG-3 to stack.}
  605.             with info^, info^.StackInfo^ do begin
  606.                 for frame := 1 to nFrames do begin
  607.                     ShowAnimatedWatch;
  608.                     CurrentSlice := frame;
  609.                     SelectSlice(CurrentSlice);
  610.                     BufferReg^ := frame - 1;
  611.                     CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  612.                     if TimeStamp then begin
  613.                         RealToString(PlotData^[frame], 9, 3, str);
  614.                         for i:=1 to 5 do
  615.                             if str[i]=' ' then str[i]:='0';
  616.                         MoveTo(2,10);
  617.                         DrawString(str);
  618.                     end; {if TimeStamp}
  619.                 end; {for}
  620.             end; {with}
  621.             BufferReg^ := 0;
  622.         end; {if LG3BufferCapture}
  623.     end;
  624.  
  625.     
  626.     
  627.     procedure MakeMovie(ShowDialog: boolean);
  628.         var
  629.             nFrames, wleft, wtop, width, height: integer;
  630.             ignore, SaveFW: integer;
  631.             OutOfMemory: boolean;
  632.             seconds: extended;
  633.             frect: rect;
  634.             Canceled: boolean;
  635.             avgFrameInterval: extended;
  636.     begin
  637.         SelectCameraWindow;
  638.         with info^ do begin
  639.             if PictureType <> FrameGrabberType then begin
  640.                     PutError('You must be capturing to make a movie.');
  641.                     exit(MakeMovie);
  642.                 end;
  643.             StopDigitizing;
  644.             if not (RoiShowing and (RoiType = RectRoi)) then begin
  645.                     PutError('Please make a rectangular selection first.');
  646.                     exit(MakeMovie);
  647.                 end;
  648.             if NotInBounds then
  649.                 exit(MakeMovie);
  650.             if ShowDialog then
  651.                 if not DoMakeMovieOptions then begin
  652.                     AbortMacro;
  653.                     exit(MakeMovie);
  654.                 end;
  655.             if (FrameGrabber <> ScionLG3) then
  656.                 LG3BufferCapture := false;
  657.             if LG3BufferCapture and (FramesWanted > MaxLG3Frames) then
  658.                 FramesWanted := MaxLG3Frames;
  659.             if LG3BufferCapture then
  660.                 BlindMovieCapture := true;
  661.             with RoiRect do begin
  662.                     left := band(left + 1, $fffc);   {Word align}
  663.                     right := band(right + 2, $fffc);
  664.                     if right > PicRect.right then
  665.                         right := PicRect.right;
  666.                     MakeRegion;
  667.                     wleft := left;
  668.                     wtop := top;
  669.                     width := right - left;
  670.                     height := bottom - top;
  671.                 end;
  672.             end; {with info^}
  673.         with frect do begin
  674.                 left := wleft;
  675.                 top := wtop;
  676.                 right := left + width;
  677.                 bottom := top + height;
  678.             end;
  679.         if UseExistingStack then begin
  680.             if not Activate('Movie') then begin
  681.                 PutError('Can''t find a stack named "Movie".');
  682.                 UseExistingStack := false;
  683.                 AbortMacro;
  684.                 exit(MakeMovie);
  685.             end;
  686.             with info^ do begin
  687.                 if (PixelsPerLine <> width) or (nLines <> height) then begin
  688.                     PutError('The dimensions of the stack "Movie" are not the same as the selection.');
  689.                     exit(MakeMovie);
  690.                 end;
  691.                 nFrames := StackInfo^.nSlices;
  692.                 if nFrames > FramesWanted then
  693.                     nFrames := FramesWanted;
  694.             end {with info}
  695.         end else begin
  696.             if not NewPicWindow('Movie', width, height) then
  697.                 exit(MakeMovie);
  698.             if not MakeStackFromWindow then
  699.                 exit(MakeMovie);
  700.             nFrames := 1;
  701.             OutOfMemory := false;
  702.             while (nFrames < FramesWanted) and (not OutOfMemory) do begin
  703.                     OutOfMemory := not AddSlice(false);
  704.                     if not OutOfMemory then
  705.                         nFrames := nFrames + 1;
  706.                 end;
  707.         end;
  708.         if ExternalTrigger and not TriggerFirstFrameOnly then
  709.             SecondsPerFrame := 0.0;
  710.         If (FramesWanted < 1) then
  711.             FramesWanted := 1;
  712.         if SecondsPerFrame < 0.0 then
  713.             SecondsPerFrame := 0.0;
  714.         with info^.StackInfo^ do begin
  715.             FrameInterval := 0.0;
  716.             StackType := movieStack;
  717.         end;
  718.         if OptionKeyWasDown then
  719.             CaptureFramesUsingTicks(SecondsPerFrame, nFrames, frect)
  720.         else
  721.             CaptureFramesUsingMicroTicks(SecondsPerFrame, nFrames, frect);
  722.         ShowFirstOrLastSlice(HomeKey);
  723.         avgFrameInterval := info^.StackInfo^.FrameInterval;
  724.         if AvgFrameInterval <> 0.0 then
  725.             ShowMessage(StringOf(nFrames:1, ' frames', cr,
  726.                 AvgFrameInterval * nFrames:1:2, ' seconds', cr,
  727. adjustinc then
  728.             inc := 0;
  729.         repeat
  730.             if adjustinc then
  731.                 inc := inc + 1;
  732.             n := trunc(slices / inc);
  733.             tmp := sqrt(n);
  734.             if trunc(tmp) <> tmp then
  735.                 tmp := trunc(tmp) + 1.0;
  736.             nColumns := trunc(tmp);
  737.             nRows := nColumns;
  738.             if (nColumns * (nRows - 1)) >= n then
  739.                 nRows := nRows - 1;
  740.             xxScale := (MaxWidth / nColumns) / StackWidth;
  741.             yyScale := (MaxHeight / nRows) / StackHeight;
  742.             if xxScale < yyScale then
  743.                 scale := xxScale
  744.             else
  745.                 scale := yyScale;
  746.             if scale > 1.0 then
  747.                 scale := 1.0;
  748.             SaveScale := scale;
  749.         until (scale >= 0.5) or (inc >= 3) or not adjustinc;
  750.     end;
  751.  
  752.     begin
  753.         InitCursor;
  754.         with info^ do begin
  755.                 StackWidth := PixelsPerLine;
  756.                 StackHeight := nLines;
  757.                 FirstSlice := 1;
  758.                 TotalSlices := StackInfo^.nSlices;
  759.                 LastSlice := TotalSlices;
  760.             end;
  761.         MaxWidth := ScreenWidth - 85;
  762.         MaxHeight := ScreenHeight - 45;
  763.         Estimate(scale, true);
  764.         IncrementSet := false;
  765.         mylog := GetNewDialog(150, nil, pointer(-1));
  766.         SetDNum(MyLog, RowsID, nRows);
  767.         SetDNum(MyLog, ColumnsID, nColumns);
  768.         SetDReal(MyLog, ScaleID, scale, 2);
  769.         SetDNum(MyLog, FirstID, FirstSlice);
  770.         SetDNum(MyLog, LastID, LastSlice);
  771.         SetDNum(MyLog, IncrementID, inc);
  772.         SetDlogItem(MyLog, NumberID, ord(gNumberSlices));
  773.         SetDlogItem(MyLog, BordersID, ord(gBorders));
  774.         OutlineButton(MyLog, ok, 16);
  775.         repeat
  776.             ModalDialog(nil, item);
  777.             if item = ColumnsID then begin
  778.                     nColumns := GetDNum(MyLog, ColumnsID);
  779.                     if nColumns < 0 then begin
  780.                             nColumns := 0;
  781.                             SetDNum(MyLog, ColumnsID, nRows);
  782.                         end;
  783.                 end;
  784.             if item = RowsID then begin
  785.                     nRows := GetDNum(MyLog, RowsID);
  786.                     if nRows < 0 then begin
  787.                             nRows := 0;
  788.                             SetDNum(MyLog, RowsID, nRows);
  789.                         end;
  790.                 end;
  791.             if item = ScaleID then
  792.                 scale := GetDReal(MyLog, ScaleID);
  793.             if item = FirstID then begin
  794.                     FirstSlice := GetDNum(MyLog, FirstID);
  795.                     if (FirstSlice < 1) or (FirstSlice > LastSlice) then
  796.                         FirstSlice := 1;
  797.                     if IncrementSet then
  798.                         Estimate(scale, false)
  799.                     else
  800.                         Estimate(scale, true);
  801.                     SetDNum(MyLog, RowsID, nRows);
  802.                     SetDNum(MyLog, ColumnsID, nColumns);
  803.                     SetDReal(MyLog, ScaleID, scale, 2);
  804.                 end;
  805.             if item = LastID then begin
  806.                     LastSlice := GetDNum(MyLog, LastID);
  807.                     if (LastSlice < FirstSlice) or (LastSlice > TotalSlices) then
  808.                         LastSlice := TotalSlices;
  809.                     if IncrementSet then
  810.                         Estimate(scale, false)
  811.                     else
  812.                         Estimate(scale, true);
  813.                     SetDNum(MyLog, RowsID, nRows);
  814.                     SetDNum(MyLog, ColumnsID, nColumns);
  815.                     SetDReal(MyLog, ScaleID, scale, 2);
  816.                 end;
  817.             if item = IncrementID then begin
  818.                     inc := GetDNum(MyLog, IncrementID);
  819.                     IncrementSet := true;
  820.                     if (inc < 1) or (inc > (slices div 2)) then begin
  821.                             inc := 1;
  822.                             SetDNum(MyLog, IncrementID, inc);
  823.                         end;
  824.                     Estimate(scale, false);
  825.                     SetDNum(MyLog, RowsID, nRows);
  826.                     SetDNum(MyLog, ColumnsID, nColumns);
  827.                     SetDReal(MyLog, ScaleID, scale, 2);
  828.                 end;
  829.             if item = NumberID then begin
  830.                     gNumberSlices := not gNumberSlices;
  831.                     SetDlogItem(MyLog, NumberID, ord(gNumberSlices));
  832.                 end;
  833.             if item = BordersID then begin
  834.                     gBorders := not gBorders;
  835.                     SetDlogItem(MyLog, BordersID, ord(gBorders));
  836.                 end;
  837.         until (item = ok) or (item = cancel);
  838.         DisposeDialog(mylog);
  839.         if item = cancel then
  840.             exit(MakeMontage);
  841.         if (scale <= 0.05) or (scale > 5) then
  842.             scale := SaveScale;
  843.         dWidth := round(StackWidth * scale);
  844.         dHeight := round(StackHeight * scale);
  845.         mWidth := nColumns * dWidth;
  846.         mHeight := nRows * dHeight;
  847.         StackInfo := info;
  848.         Background := MyGetPixel(0, 0);
  849.         SetBackgroundColor(Background);
  850.         if Background = WhiteIndex then
  851.             SetForegroundColor(BlackIndex)
  852.         else
  853.             SetForegroundColor(WhiteIndex);
  854.         if not NewPicWindow('Montage', mWidth, mHeight) then
  855.             exit(MakeMontage);
  856.         MontageInfo := info;
  857.         SaveGDevice := GetGDevice;
  858.         SetGDevice(osGDevice);
  859.         SetPort(GrafPtr(info^.osPort));
  860.         pmForeColor(ForegroundIndex);
  861.         dPort := info^.osPort;
  862.         dLeft := 0;
  863.         dTop := 0;
  864.         sPort := StackInfo^.osPort;
  865.         sRect := StackInfo^.PicRect;
  866.         i := FirstSlice;
  867.         while i <= LastSck;
  868.                 UpdateTitleBar;
  869.             end;
  870.         ResetGrayMap;
  871.     end;
  872.  
  873.  
  874.     procedure CopyGWorldToStack;
  875.     {Copies the color image stored in the 32-bit GWorld used by QuickTime
  876.      video digitizers to a 3 slice (RGB) stack.}
  877.         type
  878.             LongPtr = ^LongInt;
  879.         var
  880.             row, i, width, WatchRate: integer;
  881.             RedLine, GreenLine, BlueLine: LineType;
  882.             Pixel, RowOffset: LongInt;
  883.             pmapPtr: ptr;
  884.             LPtr, RowStart: LongPtr;
  885.     begin
  886.         if fgPixMap^^.pixelSize <> 32 then begin
  887.             PutError('RGB capture requires a 24-bit digitizer.');
  888.             DigitizerMode := digitizeColor;
  889.             exit(CopyGWorldToStack);
  890.         end;
  891.         if not MakeRGBStack(StringOf('RGB-', nPics:1)) then
  892.             exit(CopyGWorldToStack);
  893.         with info^ do begin
  894.             pmapPtr := GetPixBaseAddr(fgPixMap);
  895.             if pmapPtr = nil then
  896.                 exit(CopyGWorldToStack);
  897.             LPtr := LongPtr(pmapPtr);
  898.             RowStart := LPtr;
  899.             RowOffset := band(fgPixMap^^.RowBytes, $3FFF);
  900.             width := PicRect.right;
  901.             WatchRate := 40000 div PixelsPerLine;
  902.             for row := 0 to nLines - 1 do begin
  903.                     if (row mod WatchRate) = 0 then
  904.                         ShowAnimatedWatch;
  905.                     LPtr := RowStart;
  906.                     for i := 0 to PixelsPerLine - 1 do begin
  907.                             pixel := BitNot(LPtr^);
  908.                             blueLine[i] := band(pixel, 255);
  909.                             pixel := bsr(pixel, 8);
  910.                             greenLine[i] := band(pixel, 255);
  911.                             pixel := bsr(pixel, 8);
  912.                             redLine[i] := band(pixel, 255);
  913.                             LPtr := LongPtr(ord4(LPtr) + 4);
  914.                         end;
  915.                     RowStart := LongPtr(ord4(RowStart) + RowOffset);
  916.                     SelectSlice(1);
  917.                     PutLine(0, row, width, RedLine);
  918.                     SelectSlice(2);
  919.                     PutLine(0, row, width, GreenLine);
  920.                     SelectSlice(3);
  921.                     PutLine(0, row, width, BlueLine);
  922.                 end;
  923.             with Info^.StackInfo^ do begin
  924.                     CurrentSlice := 1;
  925.                     SelectSlice(CurrentSlice);
  926.                     StackType := rgbStack;
  927.                     UpdateTitleBar;
  928.                 end;
  929.             ResetGrayMap;
  930.         end; {with}
  931.     end;
  932.  
  933.  
  934.     procedure CaptureVDigColor;
  935.         var
  936.             err: OSErr;
  937.             pRect: rect;
  938.             thePictInfo: PictInfo;
  939.             SaveGDevice: GDHandle;
  940.     begin
  941.         if DigitizerMode = digitizeGrayscale then begin
  942.             PutError('To capture color, "8-bit Color" or "RGB Color" must be selected in Video Control.');
  943.             exit(CaptureVDigColor);
  944.         end;
  945.         if not digitizing then begin
  946.             if info^.PictureType <> FrameGrabberType then
  947.                 SelectCameraWindow;
  948.             CaptureAndDisplayFrame;
  949.         end;
  950.         if fgPixMap = nil then
  951.             exit(CaptureVDigColor);
  952.         SaveGDevice := GetGDevice;
  953.         err := GetPixMapInfo(fgPixMap, thePictInfo, ReturnColorTable, 256, SystemMethod, 0);
  954.         if err = noErr then begin
  955.             LoadColorTable(thePictInfo.theColorTable);
  956.             SetForegroundColor(BlackIndex);
  957.             SetBackgroundColor(WhiteIndex);
  958.             SetGDevice(osGDevice);
  959.             SetPort(GrafPtr(Info^.osPort));
  960.             with info^ do
  961.                 CopyBits(BitMapHandle(fgPixMap)^^, BitMapHandle(osPort^.PortPixMap)^^, picRect, picRect, DitherCopy, nil);
  962.             SetGDevice(SaveGDevice);
  963.             UpdatePicWindow;
  964.             DrawLUT;
  965.         end;
  966.         if DigitizerMode = digitizeRGB then
  967.             CopyGWorldToStack;
  968.     end;
  969.  
  970.  
  971.     procedure CaptureColor;
  972.         var
  973.             MainDevice: GDHandle;
  974.             SourcePixMap: PixMapHandle;
  975.             frame, width, height, SaveChannel: integer;
  976.             frect: rect;
  977.     begin
  978.         with info^ do
  979.             if PictureType <> FrameGrabberType then begin
  980.                     PutError('You must be capturing to capture color.');
  981.                     AbortMacro;
  982.                     exit(CaptureColor);
  983.                 end;
  984.         StopDigitizing;
  985.         if frameGrabber = QTvdig then begin
  986.             CaptureVDigColor;
  987.             exit(CaptureColor);
  988.         end;
  989.         with info^.PicRect do begin
  990.                 width := right - left;
  991.                 height := bottom - top;
  992.             end;
  993.         if Activate('RGB') then
  994.             with info^.PicRect do begin
  995.                     if ((right - left) <> width) or ((bottom - top) <> height) then
  996.                         if not MakeRGBStack('RGB') then
  997.                             exit(CaptureColor);
  998.                 end
  999.         else if not MakeRGBStack('RGB') then
  1000.             exit(CaptureColor);
  1001.         ShowWatch;
  1002.         SourcePixMap := fgPixMap;
  1003.         ResetFrameGrabber;
  1004.         with frect do begin
  1005.                 left := 0;
  1006.                 top := 0;
  1007.                 right := left + width;
  1008.                 bottom := top + height;
  1009.             end;
  1010.         ShowTriggerMessage;
  1011.         SaveChannel := VideoChannel;
  1012.         with info^, info^.StackInfo^ do begin
  1013.                 for frame := 1 to 3 do begin
  1014.                         if FrameGrabber = QuickCapture then begin
  1015.                                 case frame of
  1016.                                     1: 
  1017.                                         VideoChannel := 1; {Green}
  1018.                                     2: 
  1019.                                         VideoChannel := 0;  {Red}
  1020.                                     3: 
  1021.                                         VideoChannel := 2;  {Blue}
  1022.                                 end;
  1023.                                 ResetFrameGrabber;
  1024.                                 repeat
  1025.                                 until band(ControlReg^, $8) = 0; {mux channel not busy}
  1026.                             end
  1027.                         else begin
  1028.                                 VideoChannel := frame - 1;
  1029.                                 ResetFrameGrabber;
  1030.                             end;
  1031.                         if VideoControl <> nil then
  1032.                             ShowChannel;
  1033.                         CurrentSlice := frame;
  1034.                         SelectSlice(CurrentSlice);
  1035.                         GetFrame;
  1036.                         CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  1037.                     end; {for}
  1038.                 CurrentSlice := 1;
  1039.                 SelectSlice(CurrentSlice);
  1040.                 UpdateTitleBar;
  1041.             end; {with}
  1042.         VideoChannel := SaveChannel;
  1043.         if VideoControl <> nil then
  1044.             ShowChannel;
  1045.         ConvertRGBToEightBitColor(true);
  1046.     end;
  1047.  
  1048.  
  1049.     procedure AverageSlices;
  1050.         const
  1051.             MaxWidth = 2048;
  1052.         var
  1053.             slices, sRow, aRow, slice, i, SaveSlice: integer;
  1054.             width, height, hstart, vStart: integer;
  1055.             OldInfo, NewInfo: InfoPtr;
  1056.             aLine: LineType;
  1057.             mask: rect;
  1058.             sum: array[0..MaxWidth] of LongInt;
  1059.             AutoSelectAll: boolean;
  1060.             SlicesDiv2:LongInt;
  1061.     begin
  1062.         OldInfo := Info;
  1063.         with info^ do begin
  1064.                 if StackInfo = nil then begin
  1065.                         PutError('Average Slices requires a stack.');
  1066.                         AbortMacro;
  1067.                         exit(AverageSlices);
  1068.                     end;
  1069.                 AutoSelectAll := not Info^.RoiShowing;
  1070.                 if AutoSelectAll then
  1071.                     SelectAll(true);
  1072.                 with RoiRect do begin
  1073.                         hStart := left;
  1074.                         vStart := top;
  1075.                         width := right - left;
  1076.                         height := bottom - top;
  1077.                     end;
  1078.                 if width > MaxWidth then begin
  1079.                         PutError(concat('NIH Image can''t average selections wider than ', Long2str(MaxWidth), ' pixels.'));
  1080.                         AbortMacro;
  1081.                         exit(AverageSlices);
  1082.                     end;
  1083.                 with StackInfo^ do begin
  1084.                         slices := StackInfo^.nSlices;
  1085.                         SaveSlice := CurrentSlice;
  1086.                     end;
  1087.                 if not NewPicWindow('Average', width, height) then begin
  1088.                         AbortMacro;
  1089.                         exit(AverageSlices);
  1090.                     end;
  1091.             end;
  1092.         info^.changes := true;
  1093.         NewInfo := Info;
  1094.         aRow := 0;
  1095.         SlicesDiv2:=slices div 2; {Needed for rounding}
  1096.         for sRow := vStart to vStart + height - 1 do begin
  1097.                 info := OldInfo;
  1098.                 for i := 0 to width - 1 do
  1099.                     sum[i] := 0;
  1100.                 for slice := 1 to slices do begin
  1101.                         SelectSlice(slice);
  1102.                         GetLine(hStart, sRow, width, aLine);
  1103.                         for i := 0 to width - 1 do
  1104.                             sum[i] := sum[i] + aLine[i];
  1105.                     end;
  1106.                 for i := 0 to width - 1 do
  1107.                     aLine[i] := (sum[i]+SlicesDiv2) div slices;
  1108.                 info := NewInfo;
  1109.                 PutLine(0, aRow, width, aLine);
  1110.                 SetRect(mask, 0, aRow, width, aRow + 1);
  1111.                 aRow := aRow + 1;
  1112.                 UpdateScreen(mask);
  1113.                 if CommandPeriod then
  1114.                     leave;
  1115.             end;
  1116.         info := OldInfo;
  1117.         SelectSlice(SaveSlice);
  1118.         if AutoSelectAll then
  1119.             KillRoi;
  1120.         info:=NewInfo;
  1121.     end;
  1122.  
  1123.  
  1124.     procedure ConvertRGBToHSV;
  1125.         const
  1126.             MaxSaturation = 255;
  1127.             MaxValue = 255;
  1128.         var
  1129.             width, height, i, row, mark: integer;
  1130.             rLine, gLine, bLine, hLine, sLine, vLine: LineType;
  1131.             delta, min, max, R, G, B, H, S, V: integer;
  1132.             tmp: longint;
  1133.             UpdateR: rect;
  1134.  
  1135.         function Max3 (a, b, c: integer): integer;
  1136.             var
  1137.                 TempMax: integer;
  1138.         begin
  1139.             if (a > b) then
  1140.                 TempMax := a
  1141.             else
  1142.                 TempMax := b;
  1143.             if (TempMax > c) then
  1144.                 Max3 := TempMax
  1145.             else
  1146.                 Max3 := c;
  1147.         end;
  1148.  
  1149.         function Min3 (a, b, c: integer): integer;
  1150.             var
  1151.                 TempMin: integer;
  1152.         begin
  1153.             if (a < b) then
  1154.                 TempMin := a
  1155.             else
  1156.                 TempMin := b;
  1157.             if (TempMin < c) then
  1158.                 Min3 := TempMin
  1159.             else
  1160.                 Min3 := c;
  1161.         end;
  1162.  
  1163.     begin
  1164.         with info^ do begin
  1165.                 if StackInfo^.nSlices <> 3 then begin
  1166.                         PutError('RGB to HSV color conversion requires a three slice(red, green and blue) stack as input.');
  1167.                         exit(ConvertRGBToHSV);
  1168.                     end;
  1169.                 if Changes then begin
  1170.                         if PutMessageWithCancel('RGB to HSV color conversion is undoable.') = cancel then
  1171.                             exit(ConvertRGBToHSV);
  1172.                     end;
  1173.                 KillRoi;
  1174.                 with StackInfo^ do begin
  1175.                         CurrentSlice := 1;
  1176.                         SelectSlice(CurrentSlice);
  1177.                         UpdatePicWindow;
  1178.                     end;
  1179.                 SwitchColorTables(SpectrumItem, true);
  1180.                 title := 'HSV';
  1181.                 UpdateTitleBar;
  1182.                 width := PixelsPerLine;
  1183.                 height := nLines;
  1184.                 mark := 0;
  1185.                 ShowWatch;
  1186.                 for row := 0 to height - 1 do begin
  1187.                         SelectSlice(1);
  1188.                         GetLine(0, row, width, rLine);
  1189.                         SelectSlice(2);
  1190.                         GetLine(0, row, width, gLine);
  1191.                         SelectSlice(3);
  1192.                         GetLine(0, row, width, bLine);
  1193.                         for i := 0 to width - 1 do begin
  1194.                                 R := 255 - rLine[i];
  1195.                                 G := 255 - gLine[i];
  1196.                                 B := 255 - bLine[i];
  1197.                                 max := Max3(R, G, B);
  1198.                                 min := Min3(R, G, B);
  1199.                                 V := max;
  1200.                                 if max <> 0 then begin
  1201.                                         tmp := 255 * (max - min);
  1202.                                         S := (tmp + (tmp mod max)) div max;  {adding '(tmp mod max)' simulate rounding}
  1203.                                     end
  1204.                                 else
  1205.                                     S := 0;
  1206.                                 if S = 0 then
  1207.                                     H := 0  {undefined but, but select red }
  1208.                                 else begin
  1209.                                         delta := max - min;
  1210.                                         if R = max then begin
  1211.                                                 tmp := 85 * (G - B);
  1212.                                                 H := tmp div delta;
  1213.                                             end
  1214.                                         else if G = max then begin
  1215.                                                 tmp := 85 * (B - R);
  1216.                                                 H := 170 + tmp div delta;
  1217.                                             end
  1218.                                         else if B = max then begin
  1219.                                                 tmp := 85 * (R - G);
  1220.                                                 H := 340 + tmp div delta;
  1221.                                             end;
  1222.                                         H := H div 2;
  1223.                                         if H < 0 then
  1224.                                             H := H + 255
  1225.                                     end;
  1226.                                 if H = 0 then
  1227.                                     hLine[i] := 1
  1228.                                 else
  1229.                                     hLine[i] := H;
  1230.                                 sLine[i] := S;
  1231.                                 vLine[i] := 255 - V;
  1232.                             end;
  1233.                         SelectSlice(1);
  1234.                         PutLine(0, row, width, hLine);
  1235.                         if (row mod 10) = 0 then begin
  1236.                                 setrect(UpdateR, 0, mark, width - 1, row);
  1237.                                 mark := row;
  1238.                                 UpdateScreen(UpdateR);
  1239.                             end;
  1240.                         SelectSlice(2);
  1241.                         PutLine(0, row, width, sLine);
  1242.                         SelectSlice(3);
  1243.                         PutLine(0, row, width, vLine);
  1244.                     end;
  1245.                 SelectSlice(1);
  1246.                 StackInfo^.StackType := hsvStack;
  1247.                 UpdateTitleBar;
  1248.             end; {with}
  1249.         WhatToUndo := NothingToUndo;
  1250.     end;
  1251.  
  1252.  
  1253.     procedure DoStackInfo;
  1254.     const
  1255.         VolumeID = 5;
  1256.         MovieID = 6;
  1257.         RGBID = 7;
  1258.         HSVID = 8;
  1259.         SpacingID = 11;
  1260.         IntervalID = 12;
  1261.     var
  1262.         mylog: DialogPtr;
  1263.         item: integer;
  1264.         spacing, SaveSpacing, SaveInterval: extended;
  1265.         SaveType: StackTypeType;
  1266.         str: str255;
  1267.         
  1268.         procedure ShowStackType;
  1269.         begin
  1270.             With info^.StackInfo^ do begin
  1271.                 SetDlogItem(MyLog, VolumeID, ord(StackType = VolumeStack));
  1272.                 SetDlogItem(MyLog, MovieID, ord(StackType = MovieStack));
  1273.                 SetDlogItem(MyLog, RGBID, ord(StackType = rgbStack));
  1274.                 SetDlogItem(MyLog, HSVID, ord(StackType = hsvStack));
  1275.             end;
  1276.         end;
  1277.         
  1278.     begin
  1279.         With info^, info^.StackInfo^ do begin
  1280.             InitCursor;
  1281.             mylog := GetNewDialog(280, nil, pointer(-1));
  1282.             SaveType := StackType;
  1283.             SaveSpacing := SliceSpacing;
  1284.             SaveInterval := Frameinterval;
  1285.             ShowStackType;
  1286.             if SpatiallyCalibrated then begin
  1287.                 spacing := SliceSpacing / xScale;
  1288.                 str := xunit;
  1289.             end else begin
  1290.                 spacing := SliceSpacing;
  1291.                 str := 'pixels'
  1292.             end;
  1293.             SetDReal(MyLog, SpacingID, spacing, 3);
  1294.             ParamText(str, '', '', '');
  1295.             if Frameinterval < 99.0 then
  1296.                 SetDReal(MyLog, IntervalID, Frameinterval, 3)
  1297.             else
  1298.                 SetDReal(MyLog, IntervalID, Frameinterval, 0);
  1299.             SelectDialogItemText(MyLog, SpacingID, 0, 32767);
  1300.             OutlineButton(MyLog, ok, 16);
  1301.             repeat
  1302.                 ModalDialog(nil, item);
  1303.                 if (item >= VolumeID) and (item <= HSVID) then begin
  1304.                     case item of
  1305.                         VolumeID: StackType := VolumeStack;
  1306.                         MovieID: StackType := MovieStack;
  1307.                         rgbID: StackType := rgbStack;
  1308.                         hsvID: StackType := hsvStack;
  1309.                     end;
  1310.                     ShowStackType;
  1311.                   end;
  1312.                 if item = SpacingID then begin
  1313.                     spacing := GetDReal(MyLog, SpacingID);
  1314.                     if SpatiallyCalibrated then
  1315.                         SliceSpacing := spacing * xScale
  1316.                     else
  1317.                         SliceSpacing := spacing;
  1318.                 end;
  1319.                 if item = IntervalID then
  1320.                     Frameinterval := GetDReal(MyLog, IntervalID);
  1321.             until (item = ok) or (item = cancel);
  1322.             DisposeDialog(mylog);
  1323.             if item = cancel then begin
  1324.                 StackType := SaveType;
  1325.                 SliceSpacing := SaveSpacing;
  1326.                 Frameinterval := SaveInterval;
  1327.             end else
  1328.                 if ((StackType = rgbStack) or (StackType = hsvStack)) and (nSlices <> 3) then begin
  1329.                     PutError('RGB and HSV stacks must have three slices.');
  1330.                     StackType := SaveType;
  1331.                 end;
  1332.         end; {with}
  1333.         UpdateTitleBar;
  1334.     end;
  1335.  
  1336.  
  1337. end.